package crp.services.api.graphql.loaders;

import com.qwlabs.lang.C2;
import crp.core.resource.app.CourseResourceService;
import crp.core.resource.app.approval.ResourceApproval;
import crp.core.resource.domain.models.CourseResourceEntity;
import crp.core.resource.domain.models.ResourceEntity;
import crp.core.timetable.app.TimetableService;
import crp.core.timetable.domain.models.TimetableEntity;
import crp.core.timetable.domain.models.TimetableKey;
import crp.services.api.graphql.models.ApprovalPayload;
import crp.services.api.graphql.models.AttachmentPayload;
import crp.services.api.graphql.models.ResourcePayload;
import crp.services.api.graphql.models.SpacePayload;
import crp.services.api.graphql.models.TimetablePayload;
import io.smallrye.graphql.api.Nullable;
import org.eclipse.microprofile.graphql.GraphQLApi;
import org.eclipse.microprofile.graphql.Source;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

@GraphQLApi
@RequestScoped
public class ResourcePayloadLoader {
    private final CourseResourceService courseResourceService;
    private final TimetableService timetableService;
    private final SpaceFetcher spaceFetcher;

    @Inject
    public ResourcePayloadLoader(CourseResourceService courseResourceService,
                                 TimetableService timetableService,
                                 SpaceFetcher spaceFetcher) {
        this.courseResourceService = courseResourceService;
        this.timetableService = timetableService;
        this.spaceFetcher = spaceFetcher;
    }

    @NotNull
    public List<@Nullable SpacePayload> space(@Source List<ResourcePayload> resources) {
        return spaceFetcher.fetch(resources, ResourcePayload::getSpaceId);
    }

    public List<@Nullable TimetablePayload> timetable(@Source List<ResourcePayload> resources) {
        Set<String> resourceIds = C2.set(resources, ResourcePayload::getId);

        Map<ResourceEntity, String> resourceCourse = C2.map(
                courseResourceService.searcher().byResourceIds(resourceIds),
                CourseResourceEntity::getResource,
                CourseResourceEntity::getCourseId);

        Map<String, TimetableKey> resourceTimetableKey = new HashMap<>(resourceCourse.entrySet().size());
        resourceCourse.forEach((key, value) ->
                resourceTimetableKey.put(key.getId(), new TimetableKey(value, key.getStartAt())));
        Map<TimetableKey, TimetableEntity> result = timetableService.searcher().fetch(resourceTimetableKey.values());

        return C2.list(resources, resource -> {
            TimetableKey key = resourceTimetableKey.get(resource.getId());
            TimetableEntity timetable = result.get(key);
            return Optional.ofNullable(timetable).map(TimetablePayload::of).orElse(null);
        });
    }

    public List<@NotNull AttachmentPayload> attachment(@Source List<ResourcePayload> resources) {
        return C2.list(resources, resource -> AttachmentPayload.of(resource.getAttachment()));
    }

    public List<@Nullable ApprovalPayload> approval(@Source List<ResourcePayload> resources) {
        return C2.list(resources, resource -> ApprovalPayload.of(resource.getApproval()));
    }

    public List<@NotNull String> approvalTopic(@Source List<ResourcePayload> resources) {
        return C2.list(resources, resource -> ResourceApproval.TOPIC);
    }
}
